package org.heart.springsecurity.service.impl;

import com.auth0.jwt.JWT;
import com.auth0.jwt.JWTVerifier;
import com.auth0.jwt.algorithms.Algorithm;
import com.auth0.jwt.interfaces.DecodedJWT;
import jakarta.annotation.Resource;
import jakarta.servlet.http.HttpServletRequest;
import org.heart.springsecurity.JwtToken.TokenProvider;
import org.heart.springsecurity.customAuthenticationProvider.authenticationToken.PhoneAuthenticationToken;
import org.heart.springsecurity.enums.Client;
import org.heart.springsecurity.pojo.Login;
import org.heart.springsecurity.result.ApiResponse;
import org.heart.springsecurity.service.AuthenticationService;
import org.heart.springsecurity.springSecurity.AuthenticationUserDetails;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.BadCredentialsException;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;

import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

@Service
public class AuthenticationServiceImpl implements AuthenticationService {
    @Resource
    private AuthenticationManager authenticationManager;
    @Resource
    private RedisTemplate<Object, Object> redisTemplate;

    /**
     * @param login
     * @return
     */
    @Override
    public String login(Login login) {
        if (!StringUtils.hasText(login.getClientId())) {
            return ApiResponse.fail("系统异常");
        }
        try {
            Authentication authenticate = null;
            if (Client.PASSWORD.getClientId().equals(login.getClientId())) {
                UsernamePasswordAuthenticationToken unauthenticated = UsernamePasswordAuthenticationToken.unauthenticated(login.getEmail(), login.getPassword());
                authenticate = authenticationManager.authenticate(unauthenticated);
            } else if (Client.PHONE.getClientId().equals(login.getClientId())) {
                PhoneAuthenticationToken phoneAuthenticationToken = new PhoneAuthenticationToken(login.getPhone(), login.getCode());
                authenticate = authenticationManager.authenticate(phoneAuthenticationToken);
            }

            Object principal = null;
            if (authenticate != null) {
                principal = authenticate.getPrincipal();
            }

            AuthenticationUserDetails authenticationUserDetails = (AuthenticationUserDetails) principal;

            List<Object> collect = null;
            if (authenticationUserDetails != null) {
                collect = authenticationUserDetails.getAuthorities().stream().map(GrantedAuthority::getAuthority).collect(Collectors.toList());
            }

            HashMap<Object, Object> objectObjectHashMap = new HashMap<>();
            String accessToken = TokenProvider.createAccessToken(authenticationUserDetails.getUserId(), collect);
            String refreshToken = TokenProvider.createRefreshToken();
            objectObjectHashMap.put("accessToken", accessToken);
            objectObjectHashMap.put("refreshToken", refreshToken);
            return ApiResponse.success(200, "登录成功", objectObjectHashMap);
        } catch (AuthenticationException e) {
            //对特定异常进行捕获
            if (e instanceof BadCredentialsException) {
                return ApiResponse.fail("账号或密码错误");
            }
            //其他异常使用全局异常捕获
            /**
             * 本人对异常捕获写的很烂如果有幸 有人能看到这些代码欢迎修改优化
             * 这个项目仅仅只是一个简单的代码示例有很多逻辑和其他需要注意的东西
             * 我也只是把我学到的东西即时记录下来
             * 很多的教学不是真正意义上的前后端分离的配置（没有批评任何人只是对我来说不适合我）
             * 关于Security的详细使用可以参考官方文档我也是看文档进行学习的
             */
            throw new RuntimeException(e);

        }

    }

    /**
     * @return
     */
    @Override
    public String logout(HttpServletRequest request) {

        //获取业务和刷新token存入黑名单中
        String accessToken = request.getHeader("accessToken");
        String refreshToken = request.getHeader("refreshToken");
        if (!StringUtils.hasText(accessToken) && !StringUtils.hasText(refreshToken)) {
            return ApiResponse.fail("系统异常");
        }
        addTokenToBlacklist(accessToken);
        addTokenToBlacklist(refreshToken);
        return ApiResponse.success();
    }

    /**
     * @param request
     * @return
     */
    @Override
    public String refreshToken(HttpServletRequest request) {
        String OldToken = request.getHeader("accessToken");


        try {
            String OldRefreshToken = request.getHeader("refreshToken");
            Algorithm algorithm = Algorithm.HMAC512("refreshToken1988");
            JWTVerifier verifier = JWT.require(algorithm)
                    .withIssuer("sys").build();
            DecodedJWT verify = verifier.verify(OldRefreshToken);
            Date expiresAt = verify.getExpiresAt();
            Date now = new Date();


            long timeDifference = expiresAt.getTime() - now.getTime();
            if (timeDifference < 86400000L) { // 一天的毫秒数
                return ApiResponse.fail(4001, "当前登入已失效");
            }
            DecodedJWT decode = JWT.decode(OldToken);
            Long userId = decode.getClaim("userId").asLong();
            List<Object> scope = decode.getClaim("scope").asList(Object.class);


            HashMap<Object, Object> objectObjectHashMap = new HashMap<>(16);
            String accessToken = TokenProvider.createAccessToken(Math.toIntExact(userId), scope);
            String refreshToken = TokenProvider.createRefreshToken();
            objectObjectHashMap.put("accessToken", accessToken);
            objectObjectHashMap.put("refreshToken", refreshToken);
            //将旧的token加入黑名单
            addTokenToBlacklist(OldToken);
            addTokenToBlacklist(OldRefreshToken);
            return ApiResponse.success(200, "刷新成功", objectObjectHashMap);
        } catch (Exception exception) {
            exception.printStackTrace();
            return ApiResponse.fail(4001, "请重新登入");
        }

    }

    public void addTokenToBlacklist(String token) {
        // 设置过期时间为一天，可以根据实际情况调整
        redisTemplate.opsForValue().set(token, "blacklisted", 24, TimeUnit.HOURS);
    }


}
